// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Description: Pangea RDMA extern verbs interface for NOF AA
 */

#include <linux/errno.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/addrconf.h>
#include <linux/security.h>

#include <rdma/ib_verbs.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_addr.h>
#include <rdma/rw.h>
#include <rdma/lag.h>

#include "core_priv.h"
#include <trace/events/rdma_core.h>
#include <trace/hooks/rdma_pangea.h>

struct ib_srq *ib_create_ext_srq_user(struct ib_pd *pd,
				  struct ib_srq_init_attr *srq_init_attr,
				  struct ib_usrq_object *uobject,
				  struct ib_udata *udata,
				  int index)
{
	struct ib_srq *srq;
	struct ib_device_ex *ib_dev_ex;
	struct timespec64 start;
	int ret;

	srq = rdma_zalloc_drv_obj(pd->device, ib_srq);
	if (!srq)
		return ERR_PTR(-ENOMEM);

	srq->device = pd->device;
	srq->pd = pd;
	srq->event_handler = srq_init_attr->event_handler;
	srq->srq_context = srq_init_attr->srq_context;
	srq->srq_type = srq_init_attr->srq_type;
	srq->uobject = uobject;

	if (ib_srq_has_cq(srq->srq_type)) {
		srq->ext.cq = srq_init_attr->ext.cq;
		atomic_inc(&srq->ext.cq->usecnt);
	}
	if (srq->srq_type == IB_SRQT_XRC) {
		srq->ext.xrc.xrcd = srq_init_attr->ext.xrc.xrcd;
		atomic_inc(&srq->ext.xrc.xrcd->usecnt);
	}
	atomic_inc(&pd->usecnt);

	ib_dev_ex = ib_get_device_ex(pd->device);
	trace_ofed_monitor_exec_time_start(&start);
	ret = ib_dev_ex->ops.create_ext_srq(srq, srq_init_attr, udata, index);
	trace_ofed_monitor_exec_time_end(&start, pd->device, "create ext srq");
	if (ret) {
		atomic_dec(&srq->pd->usecnt);
		if (srq->srq_type == IB_SRQT_XRC)
			atomic_dec(&srq->ext.xrc.xrcd->usecnt);
		if (ib_srq_has_cq(srq->srq_type))
			atomic_dec(&srq->ext.cq->usecnt);
		kfree(srq);
		return ERR_PTR(ret);
	}

	return srq;
}
EXPORT_SYMBOL(ib_create_ext_srq_user);

struct ib_qp *ib_create_uhw_priv_qp(struct ib_device *dev,
					  struct ib_pd *pd,
					  struct ib_uhw_priv_qp_init_attr *priv_attr,
					  struct ib_udata *udata,
					  struct ib_uqp_object *uobj, char type)

{
	enum ib_qp_type qp_type = priv_attr->attr.qp_type;
	struct ib_qp *qp;
	struct ib_device_ex *ib_dev_ex;
	struct timespec64 start;
	bool is_xrc;

	ib_dev_ex = ib_get_device_ex(dev);
	if (!ib_dev_ex->ops.create_ext_qp) {
		ofed_log_print(PANGEA_ERR, "Device(%s) not support create ext qp.", dev->name);
		return ERR_PTR(-EOPNOTSUPP);
	}

	trace_ofed_monitor_exec_time_start(&start);
	qp = ib_dev_ex->ops.create_ext_qp(pd, priv_attr, udata);
	trace_ofed_monitor_exec_time_end(&start, dev, "create ext qp");
	if (IS_ERR(qp)) {
		ofed_log_print(PANGEA_ERR, "Device(%s) create ext qp failed, ret(%ld).",
			dev->name, PTR_ERR(qp));
		return qp;
	}

	qp->device = dev;
	qp->pd = pd;
	qp->uobject = uobj;
	qp->real_qp = qp;

	qp->qp_type = priv_attr->attr.qp_type;
	qp->rwq_ind_tbl = NULL;
	qp->send_cq = priv_attr->attr.send_cq;
	qp->recv_cq = priv_attr->attr.recv_cq;
	qp->srq = priv_attr->attr.srq;
	qp->event_handler = priv_attr->attr.event_handler;

	atomic_set(&qp->usecnt, 0);
	spin_lock_init(&qp->mr_lock);
	INIT_LIST_HEAD(&qp->rdma_mrs);
	INIT_LIST_HEAD(&qp->sig_mrs);

	rdma_restrack_new(&qp->res, RDMA_RESTRACK_QP);
	/*
	 * We don't track XRC QPs for now, because they don't have PD
	 * and more importantly they are created internaly by driver,
	 * see mlx5 create_dev_resources() as an example.
	 */
	is_xrc = qp_type == IB_QPT_XRC_INI || qp_type == IB_QPT_XRC_TGT;
	if ((qp_type < IB_QPT_MAX && !is_xrc) || qp_type == IB_QPT_DRIVER) {
		rdma_restrack_parent_name(&qp->res, &pd->res);
		rdma_restrack_add(&qp->res);
	}
	return qp;
}
EXPORT_SYMBOL(ib_create_uhw_priv_qp);

